﻿using System;
using System.Collections.Generic;

namespace _31_面向对象_结构
{
    class Program
    {
        int p;
        //public int MyProperty { get =>p; set=>p=value; } = 5; //NG
        public int MyProperty { get; set; } = 5;
        static void Main(string[] args)
        {
            //结构的使用
            // 对照组：类类型的字段，声明后必须赋值。
            //Program program;
            //Console.WriteLine(program.p);

            // 1. 使用结构时，可以不用 new 对象，能直接访问 ，没有调用任何构造函数，从而提高了分配效率
            Vector3 vector;
            vector.x = 0.5f;
            vector.y = 0.5f;
            vector.z = 0.5f;
            // 访问结构的属性前，需要 new 对象，避免某些实例成员未初始化完成。
            //vector.Y = 0.5f;
            //vector.Z = 0.5f;
            //vector.Size_p = 66; //自动属性也要new 对象后才能访问
            // 由属性的规则，我们推导出实例方法的规则：避免某些实例成员未初始化完成，在new 对象前不能访问。
            //vector.GetLenght();
            Console.WriteLine(vector.y); //如果 y 没有赋值则访问时 IED 会提示
            // 由属性的规则，我们推导出实例访问的规则：避免某些实例成员未初始化完成，在new 对象前不能访问结构的实例 。
            //Console.WriteLine(vector);

            //2. new 无参
            Vector3 vector1 = new Vector3(); // 必须自己对实例成员赋值，否则数据皆为默认值
            vector1.x = 0.55f;
            Console.WriteLine($"vector1 = {vector1}");
          
            // 3. new 有参
            Vector3 vector2 = new Vector3(0.5f, 0.6f, 0.7f);
            Console.WriteLine($"vector2 = {vector2}");

            //4. new 无参新语法糖
            //Vector3 vector3 = new Vector3 (){ };
            Vector3 vector3 = new Vector3 {Z=22f, x = 0.55f, z = 0.6f, y = 0.88f };

            Console.WriteLine("----------------分割----------------");
            Library library = new Library();

            var book = new BookInfo { name = "射雕", author = "张三" };
            var booktype = "小说";

            library[booktype] = book;

            var book2 = new BookInfo { name = "飞狐", author = "李四" };
            library[booktype] = book2;

            Console.WriteLine(library[booktype]);

        }
    }
    // 结构，关键字是 struct 
    // 结构是值类型，不同于类类型，类类型是引用类型
    // 将较小的类声明为结构，可以提高系统的处理效率。
    //vector3 三维向量
    // 结构不允许显式的写无参构造函数
    // 存在构造函数，所有字段皆需赋值，自动属性必须赋值.
    // 结构中，不能在实例字段后能赋初始值
    // 结构中，不能在实例自动属性后赋初始值
    // 结构中，私有实例成员只能在构造函数中赋初始值。
    struct Vector3
    {
        //public float x=0.5f; //struct 不能这样赋初始值
        public float x;
        public float y;
        public float z;
        private int size;
        //public int Size_p { get; set; } 
        private static int Size { get; set; } // 不强制赋值
        public float Z
        {
            get => z;
            set => z = value;
        }
        //public float Y { get; set; } = 0.5f; //struct 不能这样赋初始值
        public float Y { get; set; }

        public static Vector3 one = new Vector3(1, 1, 1);  // 实例字段不能赋初始值，但是静态的字段最好可以赋值
        public static int MyProperty { get; set; } = 10;  // 实例的自动属性不能赋初始值，但是静态的自动属性却可以(不强制要求赋值)
        //  public Vector3() { }
        static Vector3() { }  //可以写静态无参构造函数

        public Vector3(float x)
        {
            // one = new Vector3(1,1,1);
            Y = this.x = y = z = x;
            size = 10;
        }

        public Vector3(float x, float y, float z)
        {
            size = 10;
            this.x = x;
            this.y = y;
            this.z = z;
            this.Y = y;
        }

        public float GetLenght()
        {
            size = 100;
            return 10;
        }

        public static float GetSize(float x, float y)
        {
            return x * y;
        }

        public override string ToString()
        {
            return $"x={x}; y={y}；z={z}";
        }
    }

    // 结构的继承
    // （万物皆对象，结构是 ValueType，而ValueType又是继承自 object）
    // 结构不能继承结构或者类类型，但是能够继承接口，行为和class 继承接口保持一致
    struct Book { }
    //struct SBook :Book // 继承对象不能是结构
    class cBook { }
    //struct SBook :cBook // 继承对象不能是类类型
    interface IEatable
    {
        void Eat();
    }
    struct SBook<T> : IEatable // 继承对象可以是接口
    {
        public T target;
        public void Eat()
        {
        }
        //~SBook() { } //只有类类型才有析构
    }

    //  class CCB : SBook<T> { }

    // 结构与类：
    // 结构与类共享了大多数语法，结构比类有更多限制
    // 结构中，实例成员不能直接在声明的时候赋初始值，const 必须赋初始值，static 可以在字段声明后赋初始值（不强制）
    //  结构不可以显式声明无参构造函数以及析构函数(值类型要什么析构)
    //  结构变量的赋值实际上是构建新的结构实例并赋值。对新副本任何修改，不会改变源数据(明天讲struct 在 dic 中的应用注意事项)
    //  结构是值类型，而类是引用类型
    //  与类不同，结构实例化可以不用 new
    //   结构可以声明带参数的构造函数
    //  结构不能从结构或类类型继承，而且不能作为一个类的基类
    // 结构直接继承自 System.ValueType，后者继承自 System.Object
    // 结构也可以实现接口


    // 结构的集合与使用
    /// <summary>
    /// 书信息
    /// </summary>
    public struct BookInfo
    {
        public string name;
        public string author;
        public override string ToString()
        {
            return $"书名《{name}》- 作者：{author}";
        }
    }

    /// <summary>
    /// 图书馆
    /// </summary>
    public class Library
    {
        public Dictionary<string, BookInfo> books = new Dictionary<string, BookInfo>();

        public BookInfo this[string name]
        {
            get
            {
                if (!books.TryGetValue(name, out var book))
                {
                    book = default;
                }
                return book;
            }
            set
            {
                //if (books.TryGetValue(name,out var book2))
                //{
                //    // book = value; // book 在这里是局部变量，这个动作只是对局部变量赋值，无法修改字典中的对象
                //    // book 为 class （引用类型时），下面的赋值操作OK
                //    // book 为  struct（值类型时），下面的赋值操作NG
                //    // 也就是说，验证了 struct 重新声明变量，这个变量得到的不是对象的地址，而是对象的副本
                //    //  对副本赋值无法真正修改字典内的原始数据
                //    book2.name = value.name;
                //    book2.author= value.author;
                //}
                //else
                {
                    books[name] = value;
                    Console.WriteLine($"首次馆藏了书{value}");
                }
            }
        }

    }


}
